<template>
  <div :id="id" class="scroll-view">
    <div id="loader-box">
      <div id="loader"></div>
      <div id="tips" class="ml-10px">下拉刷新</div>
    </div>
    <div id="content">
      <slot></slot>
    </div>
  </div>
</template>

<script>
import { debounce } from '@/utils'

export default {
  props: {
    refresherEnabled: {
      type: Boolean,
      default: false
    },
    refresherLoaderOffsetY: {
      type: Number,
      default: 0
    }
  },
  data() {
    return {
      id: new Date().getTime() + ''
    }
  },
  mounted() {
    this.init()
  },
  beforeDestroy() {},
  methods: {
    init() {
      const { $el, refresherEnabled, refresherLoaderOffsetY } = this

      // 页面滚动到底部
      const scroll = () => {
        if ($el.scrollHeight - $el.scrollTop <= $el.clientHeight + 10) {
          this.$emit('scrolltolower')
        }
      }
      $el.addEventListener('scroll', scroll)

      // 下拉刷新
      if (!refresherEnabled) {
        return
      }

      const body = document.body
      const loaderBox = document.getElementById('loader-box')
      const loader = document.getElementById('loader')
      const tips = document.getElementById('tips')
      const content = document.getElementById('content')
      const DISTANCE_Y_MIN_LIMIT = 80
      const DISTANCE_Y_MAX_LIMIT = 150
      const DEG_LIMIT = 40

      let lastScrollTop = 0,
        isForbidDropDown = false,
        startY = 0,
        startX = 0,
        endY = 0,
        endX = 0,
        distanceY = 0,
        distanceX = 0,
        loadLock = false

      const start = (e) => {
        if (loadLock) {
          return
        }

        startY = e.touches[0].clientY
        startX = e.touches[0].clientX
      }

      const move = (e) => {
        const scrollTop = $el.scrollTop
        endY = e.touches[0].clientY
        endX = e.touches[0].clientX
        distanceY = endY - startY
        distanceX = endX - startX

        if (isForbidDropDown || loadLock || endY - startY < 0) {
          return
        }

        if (scrollTop > 0) {
          lastScrollTop = scrollTop
          return
        }

        if (scrollTop === 0 && lastScrollTop > 0) {
          isForbidDropDown = true
          setTimeout(() => {
            lastScrollTop = 0
            isForbidDropDown = false
          }, 50)
          return
        }

        const deg = Math.atan(Math.abs(distanceX) / distanceY) * (180 / Math.PI)
        if (deg > DEG_LIMIT) {
          ;[startY, startX] = [endY, endX]
          return
        }

        if (distanceY >= DISTANCE_Y_MAX_LIMIT) {
          tips.innerHTML = '释放刷新'
        } else {
          tips.innerHTML = '下拉刷新'
        }

        let percent = (100 - distanceY * 0.5) / 100
        percent = Math.max(0.5, percent)
        distanceY = distanceY * percent
        if (distanceY > DISTANCE_Y_MAX_LIMIT) {
          distanceY = DISTANCE_Y_MAX_LIMIT
        }
        loaderBox.style = `transform: translateY(${
          distanceY / 100
        }rem);transition: all 0.3s linear;`
        content.style = `transform: translateY(${distanceY / 100}rem);transition: all 0.3s linear;`
      }

      const end = () => {
        if (distanceY < 0 || distanceY < DISTANCE_Y_MIN_LIMIT) {
          loaderBox.style = `transform: translateY(0);transition: all 0.3s linear;`
          content.style = `transform: translateY(0);transition: all 0.3s linear;`
          body.className = ''
          return
        }

        if (lastScrollTop > 0 || loadLock || endY - startY < 0) {
          return
        }

        loadLock = true
        loaderBox.style = `transform: translateY(${
          (80 + refresherLoaderOffsetY) / 100
        }rem);transition: all 0.3s linear;`
        content.style = `transform: translateY(${
          (80 + refresherLoaderOffsetY) / 100
        }rem); transition: all 0.3s linear;`
        loader.className = 'loading'
        body.className = 'overflow-hidden'

        // 复位
        const refreshDone = () => {
          tips.innerHTML = '刷新完成'

          setTimeout(() => {
            startY = 0
            startX = 0
            endY = 0
            endX = 0
            distanceY = 0
            distanceX = 0
            loaderBox.style = `transform: translateY(0);transition: all 0.3s linear;`
            content.style = `transform: translateY(0);transition: all 0.3s linear;`
            setTimeout(() => {
              loader.className = ''
              tips.innerHTML = '下拉刷新'
              loadLock = false
            }, 500)
            body.className = ''
          }, 100)
        }

        setTimeout(() => {
          tips.innerHTML = '正在刷新'
        }, 100)
        setTimeout(() => {
          // 10秒后自动复位
          const timer = setTimeout(() => {
            refreshDone()
          }, 10000)
          this.$emit('refresh', () => {
            clearTimeout(timer)
            refreshDone()
          })
        }, 500)
      }

      $el.addEventListener('touchstart', start, { passive: false })
      $el.addEventListener('touchmove', debounce(move, 5), { passive: false })
      $el.addEventListener('touchend', end, { passive: false })
    },
    getScrollTop() {
      return this.$el.scrollTop
    },
    setScrollTop(scrollTop) {
      this.$el.scrollTop = scrollTop
    }
  }
}
</script>

<style lang="scss" scoped>
.scroll-view {
  position: relative;
  width: 100%;
  height: 100%;

  #loader-box {
    position: absolute;
    top: -80px;
    width: 100%;
    height: 80px;
    display: flex;
    align-items: center;
    justify-content: center;

    #loader {
      width: 25px;
      height: 25px;
      border: 3px solid #ebeef5;
      border-radius: 50%;
      border-bottom: 3px solid #51c3ff;
      transform: rotate(0deg);
    }

    #loader.loading {
      animation: loading 1s linear infinite;
    }

    @keyframes loading {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }

    #tips {
      font-size: 16px;
    }
  }
}
</style>
